#!/usr/bin/env python
import euclid
import game
import gameworld
import ai.steer.engine as steerengine
import pygame
import pygame.gfxdraw
import geom.algebra
import window.window
import geom.geom

class GameEntity(object):
    """"""
    def __init__(self, pos):
        self.pos = pos
        gameworld.instance().addEntity(self) #TODO: Could this be made in a better way??
        
        self.transform = euclid.Matrix4( )
        self.transform.identity()

    def update(self, dt):
        raise NotImplemented()
        
    def draw( self ):
        raise NotImplemented()

class MovingEntity(GameEntity):
    """"""
    def __init__(self, window, pos, mass, maxspeed, maxforce, maxturnrate):
        super( MovingEntity, self ).__init__(pos)
        self.velocity = euclid.Vector3()
        self.heading = euclid.Vector3( 1, 0, 0)
        self.side = euclid.Vector3(0, 1, 0)
        self.up = euclid.Vector3( 0, 0, 1 )
        
        self.window = window
        self.mass = mass
        self.maxSpeed = maxspeed
        self.maxForce = maxforce
        self.maxTurnRate = maxturnrate


class Boid(MovingEntity):
    """"""
    length = 20
    width = 10
    colour = ( 0, 0, 0 )
    
    def __init__(self, window,pos,  mass, maxspeed, maxforce, maxturnrate):
        super(Boid, self).__init__( window, pos, mass, maxspeed, maxforce, maxturnrate )
        self.steer = steerengine.SteerEngine( self )
        
        self.points = [ euclid.Point3( self.length / 2, 0, 0 ),
                       euclid.Point3( -self.length / 2, -self.width / 2, 0 ),
                       euclid.Point3( -self.length / 2, self.width / 2 ) ]

        self._updateTransform()
    
    def draw(self):
        pt = [ self.transform * p for p in self.points ]
        pygame.gfxdraw.filled_polygon( self.window.screen, pt, self.colour )

        #TODO: Make this optional
        self._debugDraw( )

    def update(self, dt):
        self._updateHeading( dt )
        self._updateTransform()

        EnforceZeroOverlap( self,
                            game.gameworld.instance().getEntities(
            lambda entity : entity != self and isinstance( entity, MovingEntity )
            ) )
    
    def _updateHeading(self, dt):
        _steer = self.steer.calculate()
        _accel = _steer / self.mass
        self.velocity += _accel * dt
        self.velocity = geom.algebra.truncate( self.velocity, self.maxSpeed )
        self.pos += self.velocity * dt
        
        if self.velocity.magnitude_squared() > 0.00000001:
            self.heading = self.velocity.normalized()
            
        self.pos = window.window.warp( self.window, self.pos )

    
    def _updateTransform(self):
        #TODO: This must be changed when the application is converted into 3D
        self.side = self.heading.cross( self.up )
        
        #Force the vectors into the xy-plane for 2D
        self.side.z = 0
        self.heading.z = 0
    
        self.transform = euclid.Matrix4.new_rotate_triple_axis( self.heading, self.side, self.up )
        self.transform.d = self.pos.x
        self.transform.h = self.pos.y
        self.transform.l = self.pos.z
        
    def _debugDraw( self ):
        b = self.getBound()
        pygame.gfxdraw.circle( self.window.screen, int(b.pos.x), int(b.pos.y), b.radius, (255, 0, 0) )

    def getBound(self):
        return geom.geom.Circle( self.pos, self.length / 2 )

class StaticEntiy( GameEntity ):
    def __init__( self, window, pos ):
        super(StaticEntiy, self).__init__( pos )
        self.pos = pos
        self.window = window
        self.transform = euclid.Matrix4.new_translate( self.pos.x, self.pos.y, self.pos.z )

    def update(self, dt):
        pass

    def draw(self):
        pass


class Obstacle(StaticEntiy):
    COLOUR = (175, 175, 175)
    
    def __init__( self, radius, window, pos ):
        super( Obstacle, self ).__init__(window, pos)
        self.bounds = geom.geom.Circle( pos, radius )

    def getBound(self):
        return self.bounds

    def draw(self):
        pygame.gfxdraw.circle( self.window.screen, int(self.pos.x), int(self.pos.y), int(self.getBound().radius), self.COLOUR )

class Wall(StaticEntiy):
    def __init__( self, length, dir, window, pos ):
        super(Wall, self).__init__( window, pos )

    def draw(self):
        pass

def EnforceZeroOverlap( entity, neighbours ):
    for ent in neighbours:
        overlap = ent.getBound().intersects( entity.getBound() )
        if overlap > 0:
            entity.pos = entity.pos + ( ( entity.pos - ent.pos ).normalized() * overlap )